﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Collections.ObjectModel;

namespace Yappi.HierarchicalField
{
    /// <summary>
    /// Иерархический ID
    /// </summary>
    public class HierarchicalID : IComparable<HierarchicalID>
    {
        /// <summary>
        /// Элементы уровня вложения
        /// </summary>
        private int[] levels;

        /// <summary>
        /// Иерархический идентификатор 
        /// </summary>
        /// <param name="value">Строка иерархического идентификатора</param>
        public HierarchicalID(string value)
        {
            this.levels = Parse(value);
        }

        /// <summary>
        /// Элементы идентификатора
        /// </summary>
        public Collection<int> Levels
        {
            get
            {
                return new Collection<int>(this.levels);
            }
        }

        /// <summary>
        /// Глубина элемента
        /// </summary>
        public int LevelsCount
        {
            get
            {
                return this.levels.Length;
            }
        }

        /// <summary>
        /// Индексатор к элементам идентификатора
        /// </summary>
        /// <param name="index">Индексатор элемента</param>
        /// <returns>Текущий элемент иерархии</returns>
        public int this[int index]
        {
            get { return this.levels[index]; }

            set { this.levels[index] = value; }
        }

        #region Переопределенные операторы

        /// <summary>
        /// Оператор равенства классов
        /// </summary>
        /// <param name="r1">Первый параметр</param>
        /// <param name="r2">Второй параметр</param>
        /// <returns>Истина если равны</returns>
        public static bool operator ==(HierarchicalID r1, HierarchicalID r2)
        {
            return r1.Equals(r2);
        }

        /// <summary>
        /// Оператор неравенства операторов
        /// </summary>
        /// <param name="r1">Первый параметр</param>
        /// <param name="r2">Второй параметр</param>
        /// <returns>Истина если не равны</returns>
        public static bool operator !=(HierarchicalID r1, HierarchicalID r2)
        {
            return !(r1 == r2);
        }

        /// <summary>
        /// Оператор меньше
        /// </summary>
        /// <param name="r1">Первый параметр</param>
        /// <param name="r2">Второй параметр</param>
        /// <returns>Истина если меньше</returns>
        public static bool operator <(HierarchicalID r1, HierarchicalID r2)
        {
            return r1.CompareTo(r2) < 0;
        }

        /// <summary>
        /// Оператор больше
        /// </summary>
        /// <param name="r1">Первый параметр</param>
        /// <param name="r2">Второй параметр</param>
        /// <returns>Истина если больше</returns>
        public static bool operator >(HierarchicalID r1, HierarchicalID r2)
        {
            return r1.CompareTo(r2) > 0;
        }
        #endregion

        /// <summary>
        /// Сравнение 2х элементов
        /// </summary>
        /// <param name="obj">Элемент для сравнения</param>
        /// <returns>Эквивалентен или нет</returns>
        public override bool Equals(object obj)
        {
            HierarchicalID other = obj as HierarchicalID;
            if (!(other is HierarchicalID))
                return false;
            return this.ToString().Equals(other.ToString());
        }

        /// <summary>
        /// Преобразование элемента в строку
        /// </summary>
        /// <returns>Строковое представление элемента</returns>
        public override string ToString()
        {
            List<string> stringLevels = new List<string>();
            foreach (int level in this.levels)
            {
                if (level < 10)
                    stringLevels.Add("0" + level.ToString(CultureInfo.CurrentCulture));
                else
                    stringLevels.Add(level.ToString(CultureInfo.CurrentCulture));
            }

            return String.Join(".", stringLevels.ToArray());
        }

        /// <summary>
        /// Получение Хэш кода элемента
        /// </summary>
        /// <returns>Хэш элемента</returns>
        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        #region IComparable<HierarchicalID> Members
        /// <summary>
        /// Сравнение с другим элементом
        /// </summary>
        /// <param name="other">Обьект для сравнения</param>
        /// <returns>индекс сравнения</returns>
        public int CompareTo(HierarchicalID other)
        {
            return string.Compare(this.ToString(), other.ToString(), StringComparison.Ordinal);
        }

        #endregion
        /// <summary>
        /// Разбор строки идентификатора 
        /// </summary>
        /// <param name="value">Строка иерархического идентификатора</param>
        /// <returns>Массив идентификаторов</returns>
        private static int[] Parse(string value)
        {
            string[] levels = value.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
            List<int> intValues = new List<int>();
            foreach (string level in levels)
            {
                int levelValue;
                if (int.TryParse(level, out levelValue))
                {
                    if (levelValue <= 99 && levelValue >= 0)
                        intValues.Add(levelValue);
                    else
                        throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Значение {0} содержит значения недопустимого диапазона", value));
                }
                else
                {
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Значение {0} не является правильным иерархическим идентификатором", value));
                }
            }

            return intValues.ToArray();
        }
    }
}
